/**
 * 将字节数格式化为人类可读的单位 (B, KB, MB, GB, TB)
 * @param {number} bytes - 字节数
 * @param {Object} [options={}] - 格式化选项
 * @param {number} [options.decimalPlaces=2] - 保留的小数位数，默认2
 * @param {'B' | 'KB' | 'MB' | 'GB' | 'TB'} [options.unit] - 强制使用的单位，无默认值
 * @param {boolean} [options.useBinary=true] - 是否使用二进制单位 (1024为基数)，默认true，false使用1000为基数
 * @returns 格式化后的字符串，如 "2.5 MB"
 */
export function fromBytes(
    bytes: number,
    options: { decimalPlaces?: number; unit?: 'B' | 'KB' | 'MB' | 'GB' | 'TB'; useBinary?: boolean } = {}
): string {
    if (bytes < 0) throw new Error('字节数不能为负数');
    if (bytes === 0) return '0 B';

    const { decimalPlaces = 2, unit, useBinary = true } = options;
    const base = useBinary ? 1024 : 1000;
    const units = useBinary
        ? ['B', 'KiB', 'MiB', 'GiB', 'TiB']
        : ['B', 'KB', 'MB', 'GB', 'TB'];

    // 如果指定了单位，则直接使用该单位
    if (unit) {
        const unitIndex = units.indexOf(unit);
        if (unitIndex === -1) throw new Error(`无效的单位: ${unit}`);
        const value = bytes / Math.pow(base, unitIndex);
        return `${value.toFixed(decimalPlaces)} ${unit}`;
    }

    // 自动选择最合适的单位
    const exponent = Math.min(
        Math.floor(Math.log(bytes) / Math.log(base)),
        units.length - 1
    );
    const value = bytes / Math.pow(base, exponent);
    return `${value.toFixed(decimalPlaces)} ${units[exponent]}`;
}

/**
 * 将人类可读的字节单位字符串解析为字节数
 * @param str - 带单位的字符串，如 "2.5 MB"、"1 GiB"
 * @param useBinary - 是否使用二进制单位解析 (1024为基数)，默认自动检测
 * @returns 解析后的字节数
 * @throws 当字符串格式无效时抛出错误
 */
export function toBytes(str: string, useBinary?: boolean): number {
    const match = str.match(/^\s*(\d+\.?\d*)\s*([KMGT]?i?B)\s*$/i);
    if (!match) throw new Error(`无效的字节单位字符串: ${str}`);

    const [, valueStr, unit] = match;
    const value = parseFloat(valueStr);
    if (isNaN(value)) throw new Error(`无效的数值: ${valueStr}`);

    // 自动检测是否为二进制单位 (包含 'i' 如 KiB, MiB)
    const isBinaryUnit = unit.toLowerCase().includes('i') || useBinary === true;
    const base = isBinaryUnit ? 1024 : 1000;

    const unitMap: Record<string, number> = {
        'b': 0,
        'kb': 1, 'kib': 1,
        'mb': 2, 'mib': 2,
        'gb': 3, 'gib': 3,
        'tb': 4, 'tib': 4
    };

    const exponent = unitMap[unit.toLowerCase()];
    if (exponent === undefined) throw new Error(`无效的单位: ${unit}`);

    return Math.round(value * Math.pow(base, exponent));
}

/**
 * 安全生成[0,1)范围内的随机数，并支持指定位数的精度
 * @param {number} [decimalPlaces=16] - 保留的小数位数，默认为16位
 * @returns {number} 生成的随机数，范围为[0,1)
 * @throws {Error} 当运行环境不支持加密安全随机数生成或小数位数无效时抛出错误
 * @example
 * // 生成默认精度(16位小数)的随机数
 * const randomNum = random();
 * // 生成4位小数的随机数
 * const randomNum4Decimals = random(4);
 */
export function random(decimalPlaces: number = 16): number {
    // 验证小数位数参数
    if (!Number.isInteger(decimalPlaces) || decimalPlaces < 0 || decimalPlaces > 20) {
        throw new Error('小数位数必须是0到20之间的整数');
    }

    // 使用加密安全的随机数生成器
    const crypto = window.crypto || (window as any).msCrypto;
    if (!crypto) {
        throw new Error('当前环境不支持加密安全的随机数生成');
    }

    // 生成一个32位的随机整数
    const array = new Uint32Array(1);
    crypto.getRandomValues(array);

    // 将随机整数转换为[0,1)范围内的浮点数
    // 2^32是Uint32Array能表示的最大值加1
    let randomValue = array[0] / 4294967296;

    // 根据指定的小数位数进行四舍五入
    if (decimalPlaces >= 0) {
        const factor = Math.pow(10, decimalPlaces);
        randomValue = Math.round(randomValue * factor) / factor;
    }

    return randomValue;
}

/**
 * 处理索引，确保索引为非负值
 * @param {number} length 总长度
 * @param {number} index 要处理的索引
 * @returns {number} 处理后的非负索引
 */
export function toIndex(length: number, index: number): number {
    // 处理负数索引
    while (index < 0) {
        index = length + index;
    }
    return index;
}



/**
 * 计算蛇形排列座位号
 * @param row 行号（从1开始）
 * @param col 列号（从1开始）
 * @param totalColumns 总列数（默认为8）
 * @returns 计算出的座位号
 * @example
 * // 返回 1（第一行第一列，奇数行从右到左排列）
 * getSnakeSeatNumber(1, 1, 8);
 * // 返回 9（第二行第一列，偶数行从左到右排列）
 * getSnakeSeatNumber(2, 1, 8);
 */
export function getSnakeSeatNumber(row: number, col: number, totalColumns: number = 8): number {
    // 计算当前行的起始编号
    const rowStartNumber = (row - 1) * totalColumns + 1;

    // 如果是奇数行，从右到左排列（列号从大到小）
    if (row % 2 === 1) {
        return rowStartNumber + (totalColumns - col);
    }
    // 如果是偶数行，从左到右排列（列号从小到大）
    else {
        return rowStartNumber + (col - 1);
    }
}